﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using jinek.Prometheus.Views.Converters;

namespace MvvmBase.Converters
{
    public static class TypeExpanderExtension
    {
        #region HasProperty

        private static bool SimpleHasProperty(this object obj, string property)
        {
            var propertyInfo = obj.GetType().GetProperty(property);
            return propertyInfo != null;
        }

        public static bool HasProperty(this object obj, string property)
        {
            if (obj == null) return false;
            if (String.IsNullOrEmpty(property)) return true;
            int indexOf = property.IndexOf(".", StringComparison.Ordinal);
            if (indexOf == -1) return obj.SimpleHasProperty(property);
            string firstObject = property.Substring(0, indexOf);
            if (!obj.SimpleHasProperty(firstObject)) return false;
            var fObject = obj.GetSimpleProperty(firstObject);
            string sbstr = property.Substring(indexOf + 1);
            return fObject.SimpleHasProperty(sbstr);
        }

        #endregion

        #region SetProperty

        private static void SetSimpleProperty(this object obj, string property, object value)
        {
            PropertyInfo propertyInfo = obj.GetType().GetProperty(property);
            if (propertyInfo != null)
            {
                propertyInfo.SetValue(obj, value, null);
            }
            else
            {
                throw new KeyNotFoundException(String.Format("Свойство {0} не найдено", property));
            }
        }

        public static void SetProperty(this object obj, string property, object value)
        {
            if (String.IsNullOrEmpty(property))
            {
                throw new InvalidOperationException("Херня какая-то");
                //obj = value;
            }
            int indexOf = property.IndexOf(".", StringComparison.Ordinal);
            if (indexOf == -1) obj.SetSimpleProperty(property, value);
            else
            {
                string firstObject = property.Substring(0, indexOf);
                var fObject = obj.GetSimpleProperty(firstObject);
                string sbstr = property.Substring(indexOf + 1);
                fObject.SetProperty(sbstr, value);
            }
        }

        #endregion

        #region GetProperty

        private static object GetSimpleProperty(this object obj, string property)
        {
            var propertyInfo = obj.GetType().GetProperty(property);
            if (propertyInfo != null)
            {
                object value = propertyInfo.GetValue(obj, null);
                return value;
            }
            throw new KeyNotFoundException(String.Format("Свойство {0} не найдено", property));
        }

        public static TValType GetProperty<TValType>(this object obj, string property)
        {
            return (TValType) obj.GetProperty(property);
        }

        public static object GetPropertyOrNull(this object obj, string propName)
        {
            try
            {
                return obj.GetProperty(propName);
            }
            catch
            {
                return null;
            }
        }

        public static object GetProperty(this object obj, string property)
        {
            if (String.IsNullOrEmpty(property)) return obj;
            int indexOf = property.IndexOf(".", StringComparison.Ordinal);
            if (indexOf == -1) return obj.GetSimpleProperty(property);
            string firstObject = property.Substring(0, indexOf);
            object fObject = obj.GetSimpleProperty(firstObject);
            string sbstr = property.Substring(indexOf + 1);
            return fObject.GetProperty(sbstr);
        }

        #endregion

        public static object CastOrConvert(this object obj, Type toType)
        {
            if (obj == null) return null;

            if (obj.GetType() == toType) return obj;


            if ((toType == typeof (Visibility)) && (obj is bool))
            {
                return (bool) obj ? Visibility.Visible : Visibility.Collapsed;
            }
            //Actually SilverLight does not provide it... but let it be for the future SL versions
            var typeConverterAttubute =
                toType.GetCustomAttributes(typeof (TypeConverterAttribute), true).LastOrDefault() as
                TypeConverterAttribute;
            if (typeConverterAttubute != null)
            {
                Type convType = Type.GetType(typeConverterAttubute.ConverterTypeName);
                if (convType != null)
                {
                    var constructorInfo = convType.GetConstructor(Type.EmptyTypes);
                    if (constructorInfo == null)
                        throw new InvalidOperationException(
                            "Trying to convert the value by type converter, which has no default constructor: " +
                            convType);

                    var converter = (TypeConverter) constructorInfo.Invoke(new object[0]);

                    if (converter.CanConvertFrom(toType))
                    {
                        return converter.ConvertFrom(obj);
                    }
                }
            }


            if (toType.IsEnum)
            {
                try
                {
                    return Enum.Parse(toType, obj.ToString(), false);
                }
                catch (ArgumentException)
                {
                    throw new SomethingInvalidException();
                }
            }
            if (toType.IsGenericType)
            {
                if (toType.GetGenericArguments()[0].IsEnum)
                {
                    try
                    {
                        return Enum.Parse(toType.GetGenericArguments()[0], obj.ToString(), false);
                    }
                    catch (ArgumentException)
                    {
                        throw new SomethingInvalidException();
                    }
                }
                if (toType.GetGenericTypeDefinition() == typeof (Nullable<>))
                {
                    var s = obj as string;
                    if (s != null)
                        if (s == string.Empty)
                        {
                            return null;
                        }
                    toType = toType.GetGenericArguments()[0];
                }
                var convertible = obj as IConvertible;
                if (convertible != null)
                {
                    return convertible.ToType(toType, null);
                }
                return obj; //.ToString(); the same as below: why to string ??
            }
            if (toType == typeof (ImageSource) && obj is byte[])
            {
                return ((byte[]) obj).ConvertToImageSource();
            }
            {
                var convertible = obj as IConvertible;
                if (convertible != null)
                {
                    return convertible.ToType(toType, null);
                }
                return obj /*.ToString()Why to string??*/;
            }
        }


        public static ImageSource ConvertToImageSource(this byte[] value)
        {
            //if(!(value is byte[]))throw new InvalidCastException("Необходимо передать byte[]");

            var bitmap = new BitmapImage();
            var memoryStream = new MemoryStream(value);
            bitmap.SetSource(memoryStream);
            memoryStream.Close();
            return bitmap;
        }

        public static string ToHHMMSS(this TimeSpan ts)
        {
            string mmss = string.Format("{0}:{1}", ts.Minutes, ts.Seconds.ToString(CultureInfo.InvariantCulture).PadLeft(2, '0'));

            if (ts.Hours > 0)
            {
                mmss = ts.Hours + ":" + mmss;
            }
            return mmss;
        }

        public static T[] ToArray2<T>(this ObservableCollection<T> source)
        {
            return (source).ToArray();
        }

        public static ObservableCollection<T> ToObservableCollection<T>(this IEnumerable<T> source)
        {
            var collection = new ObservableCollection<T>();
            foreach (T item in source)
            {
                collection.Add(item);
            }
            return collection;
        }
    }
}